{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "S870-bguwtbk"
      },
      "source": [
        "# Chatbot with Memory\n",
        "\n",
        "## Review\n",
        "\n",
        "[Memory](https://pmc.ncbi.nlm.nih.gov/articles/PMC10410470/) is a cognitive function that allows people to store, retrieve, and use information to understand their present and future.\n",
        "\n",
        "There are [various long-term memory types](https://langchain-ai.github.io/langgraph/concepts/memory/#memory) that can be used in AI applications.\n",
        "\n",
        "## Goals\n",
        "\n",
        "Here, we'll introduce the [LangGraph Memory Store](https://langchain-ai.github.io/langgraph/reference/store/#langgraph.store.base.BaseStore) as a way to save and retrieve long-term memories.\n",
        "\n",
        "We'll build a chatbot that uses both `short-term (within-thread)` and `long-term (across-thread)` memory.\n",
        "\n",
        "We'll focus on long-term [semantic memory](https://langchain-ai.github.io/langgraph/concepts/memory/#semantic-memory), which will be facts about the user.\n",
        "\n",
        "These long-term memories will be used to create a personalized chatbot that can remember facts about the user.\n",
        "\n",
        "It will save memory [\"in the hot path\"](https://langchain-ai.github.io/langgraph/concepts/memory/#writing-memories), as the user is chatting with it."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 1,
      "metadata": {
        "id": "B7_gaGLbwtbm"
      },
      "outputs": [],
      "source": [
        "%%capture --no-stderr\n",
        "%pip install -U langchain_openai langgraph langchain_core"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "dTQIUNoPwtbn"
      },
      "source": [
        "We'll use [LangSmith](https://docs.smith.langchain.com/) for [tracing](https://docs.smith.langchain.com/concepts/tracing)."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "metadata": {
        "id": "rr0EK1Hywtbn"
      },
      "outputs": [],
      "source": [
        "import os\n",
        "from google.colab import userdata\n",
        "\n",
        "os.environ[\"LANGCHAIN_API_KEY\"] = userdata.get('LANGCHAIN_API_KEY')\n",
        "\n",
        "os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n",
        "os.environ[\"LANGCHAIN_PROJECT\"] = \"langchain-academy\""
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "TUrIraAlwtbn"
      },
      "source": [
        "## Introduction to the LangGraph Store\n",
        "\n",
        "The [LangGraph Memory Store](https://langchain-ai.github.io/langgraph/reference/store/#langgraph.store.base.BaseStore) provides a way to store and retrieve information *across threads* in LangGraph.\n",
        "\n",
        "This is an  [open source base class](https://blog.langchain.dev/launching-long-term-memory-support-in-langgraph/) for persistent `key-value` stores."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "metadata": {
        "id": "TJUOYTB-wtbn"
      },
      "outputs": [],
      "source": [
        "import uuid\n",
        "from langgraph.store.memory import InMemoryStore\n",
        "in_memory_store = InMemoryStore()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "PuJl0x60wtbo"
      },
      "source": [
        "When storing objects (e.g., memories) in the [Store](https://langchain-ai.github.io/langgraph/reference/store/#langgraph.store.base.BaseStore), we provide:\n",
        "\n",
        "- The `namespace` for the object, a tuple (similar to directories)\n",
        "- the object `key` (similar to filenames)\n",
        "- the object `value` (similar to file contents)\n",
        "\n",
        "We use the [put](https://langchain-ai.github.io/langgraph/reference/store/#langgraph.store.base.BaseStore.put) method to save an object to the store by `namespace` and `key`."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "metadata": {
        "id": "f63-s3Fiwtbo"
      },
      "outputs": [],
      "source": [
        "# Namespace for the memory to save\n",
        "user_id = \"1\"\n",
        "namespace_for_memory = (user_id, \"memories\")\n",
        "\n",
        "# Save a memory to namespace as key and value\n",
        "key = str(uuid.uuid4())"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 5,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "btkbtu2Y0MJ8",
        "outputId": "e0683eb0-b79d-4b61-d0ac-6eeaabd08a6f"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "tuple"
            ]
          },
          "execution_count": 5,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "type(namespace_for_memory)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 6,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 35
        },
        "id": "EjrW3lbj0eKX",
        "outputId": "9ef03751-207f-48d9-a53a-634af224c9c8"
      },
      "outputs": [
        {
          "data": {
            "application/vnd.google.colaboratory.intrinsic+json": {
              "type": "string"
            },
            "text/plain": [
              "'aabc9fbf-4c56-4067-91be-49813ff7fe1f'"
            ]
          },
          "execution_count": 6,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "key"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 7,
      "metadata": {
        "id": "Gd6vHP7M0LEG"
      },
      "outputs": [],
      "source": [
        "# The value needs to be a dictionary\n",
        "value = {\"food_preference\" : \"I like pizza\"}\n",
        "\n",
        "# Save the memory\n",
        "in_memory_store.put(namespace_for_memory, key, value)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "FpHM7uiYwtbo"
      },
      "source": [
        "We use [search](https://langchain-ai.github.io/langgraph/reference/store/#langgraph.store.base.BaseStore.search) to retrieve objects from the store by `namespace`.\n",
        "\n",
        "This returns a list."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 8,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "jMbiP9jwwtbo",
        "outputId": "c7515bf9-9c7e-4413-b726-cc748a00cc4d"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "list"
            ]
          },
          "execution_count": 8,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Search\n",
        "memories = in_memory_store.search(namespace_for_memory)\n",
        "type(memories)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 9,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "L6W2qVue1BMB",
        "outputId": "6d02db39-99ee-4306-d8b3-70f1cc6160bd"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "[Item(namespace=['1', 'memories'], key='aabc9fbf-4c56-4067-91be-49813ff7fe1f', value={'food_preference': 'I like pizza'}, created_at='2025-01-02T16:21:29.887022+00:00', updated_at='2025-01-02T16:21:29.887028+00:00', score=None)]"
            ]
          },
          "execution_count": 9,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "memories"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 10,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "b5XhyaNuwtbo",
        "outputId": "bb8c1452-7c76-4e99-a4ac-e30d7ff338d1"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "{'namespace': ['1', 'memories'],\n",
              " 'key': 'aabc9fbf-4c56-4067-91be-49813ff7fe1f',\n",
              " 'value': {'food_preference': 'I like pizza'},\n",
              " 'created_at': '2025-01-02T16:21:29.887022+00:00',\n",
              " 'updated_at': '2025-01-02T16:21:29.887028+00:00',\n",
              " 'score': None}"
            ]
          },
          "execution_count": 10,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Metatdata\n",
        "memories[0].dict()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 11,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "JlQ01AOAwtbp",
        "outputId": "d67cd95b-2c41-440c-d320-5e408a07cb6c"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "aabc9fbf-4c56-4067-91be-49813ff7fe1f {'food_preference': 'I like pizza'}\n"
          ]
        }
      ],
      "source": [
        "# The key, value\n",
        "print(memories[0].key, memories[0].value)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "OdJq-Xbgwtbp"
      },
      "source": [
        "We can also use [get](https://langchain-ai.github.io/langgraph/reference/store/#langgraph.store.base.BaseStore.get) to retrieve an object by `namespace` and `key`."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 12,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 35
        },
        "id": "1lf_u87W1lZO",
        "outputId": "9fb22142-104f-4ba3-9969-e7e49dd7056c"
      },
      "outputs": [
        {
          "data": {
            "application/vnd.google.colaboratory.intrinsic+json": {
              "type": "string"
            },
            "text/plain": [
              "'aabc9fbf-4c56-4067-91be-49813ff7fe1f'"
            ]
          },
          "execution_count": 12,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "key"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 13,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "aUhhAiz_wtbp",
        "outputId": "56513bff-b21c-4690-d324-393d4781ec26"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "{'namespace': ['1', 'memories'],\n",
              " 'key': 'aabc9fbf-4c56-4067-91be-49813ff7fe1f',\n",
              " 'value': {'food_preference': 'I like pizza'},\n",
              " 'created_at': '2025-01-02T16:21:29.887022+00:00',\n",
              " 'updated_at': '2025-01-02T16:21:29.887028+00:00'}"
            ]
          },
          "execution_count": 13,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Get the memory by namespace and key\n",
        "memory = in_memory_store.get(namespace_for_memory, key)\n",
        "memory.dict()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "ij7LCnTFwtbp"
      },
      "source": [
        "## Chatbot with long-term memory\n",
        "\n",
        "We want a chatbot that [has two types of memory](https://docs.google.com/presentation/d/181mvjlgsnxudQI6S3ritg9sooNyu4AcLLFH1UK0kIuk/edit#slide=id.g30eb3c8cf10_0_156):\n",
        "\n",
        "1. `Short-term (within-thread) memory`: Chatbot can persist conversational history and / or allow interruptions in a chat session.\n",
        "2. `Long-term (cross-thread) memory`: Chatbot can remember information about a specific user *across all chat sessions*."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 14,
      "metadata": {
        "id": "i1E9ecTHwtbp"
      },
      "outputs": [],
      "source": [
        "os.environ[\"GOOGLE_API_KEY\"] = userdata.get('GOOGLE_API_KEY')"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "y2wR0egqwtbp"
      },
      "source": [
        "For `short-term memory`, we'll use a [checkpointer](https://langchain-ai.github.io/langgraph/concepts/persistence/#checkpointer-libraries).\n",
        "\n",
        "See Module 2 and our [conceptual docs](https://langchain-ai.github.io/langgraph/concepts/persistence/) for more on checkpointers, but in summary:\n",
        "\n",
        "* They write the graph state at each step to a thread.\n",
        "* They persist the chat history in the thread.\n",
        "* They allow the graph to be interrupted and / or resumed from any step in the thread.\n",
        "\n",
        "And, for `long-term memory`, we'll use the [LangGraph Store](https://langchain-ai.github.io/langgraph/reference/store/#langgraph.store.base.BaseStore) as introduced above."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 16,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "4-NRnwRqwtbp",
        "outputId": "44a09fd3-fd26-4341-c889-32da73f65910"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "\u001b[?25l   \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m0.0/41.3 kB\u001b[0m \u001b[31m?\u001b[0m eta \u001b[36m-:--:--\u001b[0m\r\u001b[2K   \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m41.3/41.3 kB\u001b[0m \u001b[31m1.4 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
            "\u001b[?25h"
          ]
        }
      ],
      "source": [
        "%pip install -qU langchain-google-genai"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 18,
      "metadata": {
        "id": "Kk745WDh3Gbd"
      },
      "outputs": [],
      "source": [
        "from langchain_google_genai import ChatGoogleGenerativeAI\n",
        "\n",
        "model = ChatGoogleGenerativeAI(\n",
        "    model=\"gemini-1.5-flash\",\n",
        "    temperature=0,\n",
        "    max_tokens=None,\n",
        "    timeout=None,\n",
        "    max_retries=2,\n",
        "    # other params...\n",
        ")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 19,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "dtzNrTgd3lEZ",
        "outputId": "b4594429-f96b-47a8-8886-7f80c238a071"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "AIMessage(content='Hi there! How can I help you today?\\n', additional_kwargs={}, response_metadata={'prompt_feedback': {'block_reason': 0, 'safety_ratings': []}, 'finish_reason': 'STOP', 'safety_ratings': []}, id='run-09a23a74-a041-4912-be98-42700ec92ebd-0', usage_metadata={'input_tokens': 2, 'output_tokens': 11, 'total_tokens': 13, 'input_token_details': {'cache_read': 0}})"
            ]
          },
          "execution_count": 19,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "model.invoke(\"hi\")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "sbaC6G8Dwtbq"
      },
      "source": [
        "The chat history will be saved to short-term memory using the checkpointer.\n",
        "\n",
        "The chatbot will reflect on the chat history.\n",
        "\n",
        "It will then create and save a memory to the [LangGraph Store](https://langchain-ai.github.io/langgraph/reference/store/#langgraph.store.base.BaseStore).\n",
        "\n",
        "This memory is accessible in future chat sessions to personalize the chatbot's responses."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 20,
      "metadata": {
        "id": "gYa8K7Vw3-nq"
      },
      "outputs": [],
      "source": [
        "from IPython.display import Image, display\n",
        "\n",
        "from langgraph.checkpoint.memory import MemorySaver\n",
        "from langgraph.graph import StateGraph, MessagesState, START, END\n",
        "from langgraph.store.base import BaseStore\n",
        "\n",
        "from langchain_core.messages import HumanMessage, SystemMessage\n",
        "from langchain_core.runnables.config import RunnableConfig\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 21,
      "metadata": {
        "id": "kGnOqp9N4CQm"
      },
      "outputs": [],
      "source": [
        "# Chatbot instruction\n",
        "MODEL_SYSTEM_MESSAGE = \"\"\"You are a helpful assistant with memory that provides information about the user.\n",
        "If you have memory for this user, use it to personalize your responses.\n",
        "Here is the memory (it may be empty): {memory}\"\"\"\n",
        "\n",
        "# Create new memory from the chat history and any existing memory\n",
        "CREATE_MEMORY_INSTRUCTION = \"\"\"\"You are collecting information about\n",
        "the user to personalize your responses.\n",
        "\n",
        "CURRENT USER INFORMATION:\n",
        "{memory}\n",
        "\n",
        "INSTRUCTIONS:\n",
        "1. Review the chat history below carefully\n",
        "2. Identify new information about the user, such as:\n",
        "   - Personal details (name, location)\n",
        "   - Preferences (likes, dislikes)\n",
        "   - Interests and hobbies\n",
        "   - Past experiences\n",
        "   - Goals or future plans\n",
        "3. Merge any new information with existing memory\n",
        "4. Format the memory as a clear, bulleted list\n",
        "5. If new information conflicts with existing memory, keep the most recent version\n",
        "\n",
        "Remember: Only include factual information directly stated by the user. Do not make assumptions or inferences.\n",
        "\n",
        "Based on the chat history below, please update the user information:\"\"\"\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 22,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 350
        },
        "id": "epNNguj5wtbq",
        "outputId": "e507aaf4-08d6-415b-974e-968e26dbaae1"
      },
      "outputs": [
        {
          "data": {
            "image/png": "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",
            "text/plain": [
              "<IPython.core.display.Image object>"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "def call_model(state: MessagesState, config: RunnableConfig, store: BaseStore):\n",
        "\n",
        "    \"\"\"Load memory from the store and use it to personalize the chatbot's response.\"\"\"\n",
        "\n",
        "    # Get the user ID from the config\n",
        "    user_id = config[\"configurable\"][\"user_id\"]\n",
        "\n",
        "    # Retrieve memory from the store\n",
        "    namespace = (\"memory\", user_id)\n",
        "    key = \"user_memory\"\n",
        "    existing_memory = store.get(namespace, key)\n",
        "\n",
        "    # Extract the actual memory content if it exists and add a prefix\n",
        "    if existing_memory:\n",
        "        # Value is a dictionary with a memory key\n",
        "        existing_memory_content = existing_memory.value.get('memory')\n",
        "    else:\n",
        "        existing_memory_content = \"No existing memory found.\"\n",
        "\n",
        "    # Format the memory in the system prompt\n",
        "    system_msg = MODEL_SYSTEM_MESSAGE.format(memory=existing_memory_content)\n",
        "\n",
        "    # Respond using memory as well as the chat history\n",
        "    response = model.invoke([SystemMessage(content=system_msg)]+state[\"messages\"])\n",
        "\n",
        "    return {\"messages\": response}\n",
        "\n",
        "def write_memory(state: MessagesState, config: RunnableConfig, store: BaseStore):\n",
        "\n",
        "    \"\"\"Reflect on the chat history and save a memory to the store.\"\"\"\n",
        "\n",
        "    # Get the user ID from the config\n",
        "    user_id = config[\"configurable\"][\"user_id\"]\n",
        "\n",
        "    # Retrieve existing memory from the store\n",
        "    namespace = (\"memory\", user_id)\n",
        "    existing_memory = store.get(namespace, \"user_memory\")\n",
        "\n",
        "    # Extract the memory\n",
        "    if existing_memory:\n",
        "        existing_memory_content = existing_memory.value.get('memory')\n",
        "    else:\n",
        "        existing_memory_content = \"No existing memory found.\"\n",
        "\n",
        "    # Format the memory in the system prompt\n",
        "    system_msg = CREATE_MEMORY_INSTRUCTION.format(memory=existing_memory_content)\n",
        "    new_memory = model.invoke([SystemMessage(content=system_msg)]+state['messages'])\n",
        "\n",
        "    # Overwrite the existing memory in the store\n",
        "    key = \"user_memory\"\n",
        "\n",
        "    # Write value as a dictionary with a memory key\n",
        "    store.put(namespace, key, {\"memory\": new_memory.content})\n",
        "\n",
        "# Define the graph\n",
        "builder = StateGraph(MessagesState)\n",
        "builder.add_node(\"call_model\", call_model)\n",
        "builder.add_node(\"write_memory\", write_memory)\n",
        "builder.add_edge(START, \"call_model\")\n",
        "builder.add_edge(\"call_model\", \"write_memory\")\n",
        "builder.add_edge(\"write_memory\", END)\n",
        "\n",
        "# Store for long-term (across-thread) memory\n",
        "across_thread_memory = InMemoryStore()\n",
        "\n",
        "# Checkpointer for short-term (within-thread) memory\n",
        "within_thread_memory = MemorySaver()\n",
        "\n",
        "# Compile the graph with the checkpointer fir and store\n",
        "graph = builder.compile(checkpointer=within_thread_memory, store=across_thread_memory)\n",
        "\n",
        "# View\n",
        "display(Image(graph.get_graph(xray=1).draw_mermaid_png()))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "1U0syvKTwtbq"
      },
      "source": [
        "When we interact with the chatbot, we supply two things:\n",
        "\n",
        "1. `Short-term (within-thread) memory`: A `thread ID` for persisting the chat history.\n",
        "2. `Long-term (cross-thread) memory`: A `user ID` to namespace long-term memories to the user.\n",
        "\n",
        "Let's see how these work together in practice."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 23,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "l4V1RXBzwtbq",
        "outputId": "5faf7135-271d-46dd-fadc-1c5fb5293148"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "================================\u001b[1m Human Message \u001b[0m=================================\n",
            "\n",
            "Hi, my name is Lance\n",
            "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
            "\n",
            "Hello Lance, it's nice to meet you!  I'll remember that for our future conversations.\n"
          ]
        }
      ],
      "source": [
        "# We supply a thread ID for short-term (within-thread) memory\n",
        "# We supply a user ID for long-term (across-thread) memory\n",
        "config = {\"configurable\": {\"thread_id\": \"1\", \"user_id\": \"1\"}}\n",
        "\n",
        "# User input\n",
        "input_messages = [HumanMessage(content=\"Hi, my name is Lance\")]\n",
        "\n",
        "# Run the graph\n",
        "for chunk in graph.stream({\"messages\": input_messages}, config, stream_mode=\"values\"):\n",
        "    chunk[\"messages\"][-1].pretty_print()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 24,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "GLLun70dwtbr",
        "outputId": "1659ad88-d035-4a9a-8e0d-675f04979e36"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "================================\u001b[1m Human Message \u001b[0m=================================\n",
            "\n",
            "I like to bike around San Francisco\n",
            "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
            "\n",
            "That's great, Lance! San Francisco is a beautiful city to explore by bike, although I imagine it can be quite hilly.  Are there any particular routes or neighborhoods you enjoy biking in?\n"
          ]
        }
      ],
      "source": [
        "# User input\n",
        "input_messages = [HumanMessage(content=\"I like to bike around San Francisco\")]\n",
        "\n",
        "# Run the graph\n",
        "for chunk in graph.stream({\"messages\": input_messages}, config, stream_mode=\"values\"):\n",
        "    chunk[\"messages\"][-1].pretty_print()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "SnlD7VvFwtbr"
      },
      "source": [
        "We're using the `MemorySaver` checkpointer for within-thread memory.\n",
        "\n",
        "This saves the chat history to the thread.\n",
        "\n",
        "We can look at the chat history saved to the thread."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 25,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "kD95QOJlwtbr",
        "outputId": "5bc5be97-b527-4006-e9af-0f4f4c7bcffd"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "================================\u001b[1m Human Message \u001b[0m=================================\n",
            "\n",
            "Hi, my name is Lance\n",
            "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
            "\n",
            "Hello Lance, it's nice to meet you!  I'll remember that for our future conversations.\n",
            "================================\u001b[1m Human Message \u001b[0m=================================\n",
            "\n",
            "I like to bike around San Francisco\n",
            "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
            "\n",
            "That's great, Lance! San Francisco is a beautiful city to explore by bike, although I imagine it can be quite hilly.  Are there any particular routes or neighborhoods you enjoy biking in?\n"
          ]
        }
      ],
      "source": [
        "thread = {\"configurable\": {\"thread_id\": \"1\"}}\n",
        "state = graph.get_state(thread).values\n",
        "for m in state[\"messages\"]:\n",
        "    m.pretty_print()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "bPA4g44Gwtbr"
      },
      "source": [
        "Recall that we compiled the graph with our the store:\n",
        "\n",
        "```python\n",
        "across_thread_memory = InMemoryStore()\n",
        "```\n",
        "\n",
        "And, we added a node to the graph (`write_memory`) that reflects on the chat history and saves a memory to the store.\n",
        "\n",
        "We can to see if the memory was saved to the store."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 26,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "_KgcD4fXwtbr",
        "outputId": "47496490-e686-46ba-afee-4bfd82bbce40"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "{'namespace': ['memory', '1'],\n",
              " 'key': 'user_memory',\n",
              " 'value': {'memory': \"Here's an updated summary of what I know about you:\\n\\n* **Name:** Lance\\n* **Location:** San Francisco\\n* **Interests/Hobbies:** Biking\\n\\n\"},\n",
              " 'created_at': '2025-01-02T16:55:12.626372+00:00',\n",
              " 'updated_at': '2025-01-02T16:55:12.626375+00:00'}"
            ]
          },
          "execution_count": 26,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Namespace for the memory to save\n",
        "user_id = \"1\"\n",
        "namespace = (\"memory\", user_id)\n",
        "existing_memory = across_thread_memory.get(namespace, \"user_memory\")\n",
        "existing_memory.dict()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "8VoCq6tvwtbr"
      },
      "source": [
        "Now, let's kick off a *new thread* with the *same user ID*.\n",
        "\n",
        "We should see that the chatbot remembered the user's profile and used it to personalize the response."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 28,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "s2kf99MHwtbs",
        "outputId": "6bd24f10-f85e-4e82-81f7-6188de69e33e"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "================================\u001b[1m Human Message \u001b[0m=================================\n",
            "\n",
            "Hi! What do you know about me?\n",
            "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
            "\n",
            "Hi Lance!  I know you're from San Francisco and that you enjoy biking.  Is there anything else I should know about you?\n"
          ]
        }
      ],
      "source": [
        "# We supply a user ID for across-thread memory as well as a new thread ID\n",
        "config = {\"configurable\": {\"thread_id\": \"245433444\", \"user_id\": \"1\"}}\n",
        "\n",
        "# User input\n",
        "input_messages = [HumanMessage(content=\"Hi! What do you know about me?\")]\n",
        "\n",
        "# Run the graph\n",
        "for chunk in graph.stream({\"messages\": input_messages}, config, stream_mode=\"values\"):\n",
        "    chunk[\"messages\"][-1].pretty_print()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "BWrVi5sewtbs",
        "outputId": "28306b2b-37a1-43b3-8a51-c41f1fa9820a"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "================================\u001b[1m Human Message \u001b[0m=================================\n",
            "\n",
            "Great, are there any bakeries nearby that I can check out? I like a croissant after biking.\n",
            "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
            "\n",
            "Absolutely, Lance! Here are a few bakeries in San Francisco where you can enjoy a delicious croissant after your ride:\n",
            "\n",
            "1. **Tartine Bakery**: Located in the Mission District, Tartine is famous for its pastries, and their croissants are a must-try.\n",
            "\n",
            "2. **Arsicault Bakery**: This bakery in the Richmond District has been praised for its buttery, flaky croissants. It's a bit of a detour, but worth it!\n",
            "\n",
            "3. **b. Patisserie**: Situated in Lower Pacific Heights, b. Patisserie offers a variety of pastries, and their croissants are particularly popular.\n",
            "\n",
            "4. **Le Marais Bakery**: With locations in the Marina and Castro, Le Marais offers a charming French bakery experience with excellent croissants.\n",
            "\n",
            "5. **Neighbor Bakehouse**: Located in the Dogpatch, this bakery is known for its creative pastries, including some fantastic croissants.\n",
            "\n",
            "These spots should provide a delightful treat after your biking adventures. Enjoy your ride and your croissant!\n"
          ]
        }
      ],
      "source": [
        "# User input\n",
        "input_messages = [HumanMessage(content=\"Great, are there any bakeries nearby that I can check out? I like a croissant after biking.\")]\n",
        "\n",
        "# Run the graph\n",
        "for chunk in graph.stream({\"messages\": input_messages}, config, stream_mode=\"values\"):\n",
        "    chunk[\"messages\"][-1].pretty_print()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "pW4AV3Lj-rnJ"
      },
      "source": [
        "## Assignment\n",
        "\n",
        "1. Add Long Term Memoery to 01 using interrupt and long term memory."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "BppltRYHwtbs"
      },
      "source": [
        "## Viewing traces in LangSmith\n",
        "\n",
        "We can see that the memories are retrieved from the store and supplied as part of the system prompt, as expected:\n",
        "\n",
        "https://smith.langchain.com/public/10268d64-82ff-434e-ac02-4afa5cc15432/r"
      ]
    }
  ],
  "metadata": {
    "colab": {
      "provenance": []
    },
    "kernelspec": {
      "display_name": "Python 3 (ipykernel)",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3.11.8"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}
